﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Windows.Controls;
using System.Xml.Serialization;

namespace Deployment.Data.Environment.Connection
{
    [Serializable()]
    public abstract class AbstractConnection : NotifyPropertyChanged, IComparable<AbstractConnection>
    {
        //private bool _isDataValid;
        private bool _connectionTestedSuccessfully;
        private string _name;
        private string _data;

        private bool _isValidating;
        private string _failureMessage = null;

        protected string _TypeDescription = "Description not defined.";

        public static List<AbstractConnection> GetAvailableConnectionTypes()
        {
            List<AbstractConnection> ConnectionTypes = new List<AbstractConnection>();
            Assembly asm = Assembly.GetExecutingAssembly();
            FileInfo AssemblyFile = new FileInfo(asm.Location);
            DirectoryInfo di = AssemblyFile.Directory;
            FileInfo[] files = di.GetFiles("*.dll", SearchOption.AllDirectories);
            foreach (FileInfo file in files)
            {
                Assembly asmFile = Assembly.LoadFrom(file.FullName);
                Type[] types = asmFile.GetTypes();
                for (int i = 0; i < types.Length; i++)
                {
                    if (types[i].IsSubclassOf(typeof(AbstractConnection)) && !types[i].IsAbstract)
                    {
                        AbstractConnection obj = asmFile.CreateInstance(types[i].FullName) as AbstractConnection;
                        if (obj != null)
                            ConnectionTypes.Add(obj);
                    }
                }
            }
            return ConnectionTypes;
        }

        [XmlAttribute]
        public string Name
        {
            get { return _name; }
            set
            {
                if (_name != value)
                {
                    _name = value;
                    OnPropertyChanged("Name");
                }
            }
        }

        [XmlElement]
        public string Data
        {
            get { return _data; }
            set
            {
                if (string.IsNullOrEmpty(value))
                    throw new ArgumentNullException("Data Property cannot be set to null or empty string.");
                if (!string.Equals(_data, value, StringComparison.CurrentCulture))
                {
                    _data = value;
                    OnPropertyChanged("Data");
                    if (!IsInternalDataUpdate)
                        onDataUpdated();
                    OnPropertyChanged("IsConnectionDataValid");
                    IsInternalDataUpdate = false;
                }
            }
        }

        [XmlIgnore]
        public string TypeDescription
        {
            get { return _TypeDescription; }
        }

        [XmlIgnore]
        protected internal bool IsInternalDataUpdate { get; set; }

        //UserControl _editor = null;

        [XmlIgnore]
        public UserControl EditorControl
        {
            get
            {
                UserControl editor = createControl();
                editor.DataContext = this;
                return editor;
            }
        }

        [XmlIgnore]
        public bool IsConnectionDataValid
        {
            get { return this.connectionDataValid(); }
            //protected internal set
            //{
            //    if (this._isDataValid != value)
            //    {
            //        _isDataValid = value;
            //        OnPropertyChanged("IsConnectionDataValid");
            //    }
            //}
        }

        [XmlIgnore]
        public Boolean ConnectionTestedSuccessfully
        {
            get { return _connectionTestedSuccessfully; }
            protected internal set
            {
                if (_connectionTestedSuccessfully != value)
                {
                    _connectionTestedSuccessfully = value;
                    OnPropertyChanged("ConnectionTestedSuccessfully");
                }
            }
        }

        [XmlIgnore]
        public bool IsConnectionTesting
        {
            get { return _isValidating;}
            private set
            {
                if (_isValidating != value)
                {
                    _isValidating = value;
                    OnPropertyChanged("IsConnectionValidating");
                }
            }
        }

        [XmlIgnore]
        public string FailureMessage
        {
            get { return _failureMessage; }
            private set
            {
                if (_failureMessage != value)
                {
                    _failureMessage = value;
                    OnPropertyChanged("FailureMessage");
                }
            }
        }

        public void ValidateConnection()
        {
            if (!IsConnectionTesting)
            {
                BackgroundWorker worker = new BackgroundWorker();
                worker.DoWork += ValidationWorker_DoWork;
                worker.RunWorkerCompleted += ValidationWorker_RunWorkerCompleted;
                IsConnectionTesting = true;
                worker.RunWorkerAsync();
            }
        }

        private void ValidationWorker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
        {
            if (!e.Cancelled)
            {
                Validation result = e.Result as Validation;
                if (result != null)
                {
                    FailureMessage = result.FailureMessage;
                    ConnectionTestedSuccessfully = result.result;
                }
            }
            else
            {
                FailureMessage = "Validation Canceled";
                ConnectionTestedSuccessfully = false;
            }
            IsConnectionTesting = false;
        }

        private void ValidationWorker_DoWork(object sender, DoWorkEventArgs e)
        {
            string message = null;
            bool value = PerformConnectionValidation(out message);

            Validation v = new Validation();
            v.result = value;
            v.FailureMessage = message;
            e.Result = v;
        }

        private class Validation
        {
            internal bool result;
            internal string FailureMessage;
        }

        protected internal abstract bool connectionDataValid();

        protected internal abstract UserControl createControl();

        protected internal abstract void onDataUpdated();

        protected internal abstract bool PerformConnectionValidation(out string message);

        public AbstractConnection Clone()
        {
            AbstractConnection cloned = Activator.CreateInstance(GetType()) as AbstractConnection;
            cloned.Name = Name;
            cloned.Data = Data;
            return cloned;
        }

        public int CompareTo(AbstractConnection other)
        {
            if (other == null)
                return 1;
            return string.Compare(this.Name, other.Name, true);
        }

        
    }
}
